package com.recipedb.recipedb.service.impl;

import com.recipedb.recipedb.entity.*;
import com.recipedb.recipedb.repository.*;
import com.recipedb.recipedb.service.LikesService;
import com.recipedb.recipedb.service.RecipeRecommendationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional
public class RecipeRecommendationServiceImpl implements RecipeRecommendationService {

    @Autowired
    private RecipeRepository recipeRepository;

    @Autowired
    private LikesService likesService;

    @Autowired
    private TagRepository tagRepository;

    @Autowired
    private CategoryRepository categoryRepository;

    // 时间段权重配置
    private static class TimePeriodWeight {
        LocalTime start;
        LocalTime end;
        double weight;

        TimePeriodWeight(LocalTime start, LocalTime end, double weight) {
            this.start = start;
            this.end = end;
            this.weight = weight;
        }

        boolean contains(LocalTime time) {
            if (start.isBefore(end)) {
                return !time.isBefore(start) && !time.isAfter(end);
            } else {
                return !time.isBefore(start) || !time.isAfter(end);
            }
        }
    }

    private final Map<String, TimePeriodWeight> timeWeights = Map.of(
            "breakfast", new TimePeriodWeight(LocalTime.of(6, 0), LocalTime.of(10, 0), 1.2),
            "lunch", new TimePeriodWeight(LocalTime.of(11, 0), LocalTime.of(13, 0), 1.5),
            "dinner", new TimePeriodWeight(LocalTime.of(17, 0), LocalTime.of(20, 0), 1.3),
            "snack", new TimePeriodWeight(LocalTime.of(14, 0), LocalTime.of(16, 0), 0.8),
            "late_night", new TimePeriodWeight(LocalTime.of(21, 0), LocalTime.of(23, 59), 0.7),
            "early_morning", new TimePeriodWeight(LocalTime.of(0, 0), LocalTime.of(5, 59), 0.5)
    );

    @Override
    public List<Recipe> recommendTop5Recipes(Long userId) {
        // 获取所有食谱
        List<Recipe> allRecipes = recipeRepository.findAll();

        if (allRecipes.isEmpty()) {
            return Collections.emptyList();
        }

        // 对所有食谱计算分数并排序，取前5名
        return allRecipes.stream()
                .map(recipe -> {
                    double score = calculateRecipeScore(userId, recipe);
                    return new AbstractMap.SimpleEntry<>(recipe, score);
                })
                .sorted((e1, e2) -> Double.compare(e2.getValue(), e1.getValue()))
                .limit(5)
                .map(AbstractMap.SimpleEntry::getKey)
                .collect(Collectors.toList());
    }

    @Override
    public List<Recipe> Discover5Recipe(Long userId) {
        // 获取所有食谱
        List<Recipe> allRecipes = recipeRepository.findAll();

        if (allRecipes.isEmpty()) {
            return Collections.emptyList();
        }

        // 获取用户已点赞的食谱ID
        Set<Long> likedRecipeIds = getLikedRecipeIds(userId);

        // 计算所有食谱的最大点赞数，用于归一化
        int maxLikes = allRecipes.stream()
                .mapToInt(Recipe::getLikesCount)
                .max()
                .orElse(1);

        // 对所有食谱计算分数并排序，取前5名
        return allRecipes.stream()
                .filter(recipe -> !likedRecipeIds.contains(recipe.getId())) // 过滤已点赞的食谱
                .map(recipe -> {
                    double score = calculateTimeAndLikeScore(recipe, maxLikes);
                    return new AbstractMap.SimpleEntry<>(recipe, score);
                })
                .sorted((e1, e2) -> Double.compare(e2.getValue(), e1.getValue()))
                .limit(5)
                .map(AbstractMap.SimpleEntry::getKey)
                .collect(Collectors.toList());
    }

    /**
     * 计算基于时段和点赞数的推荐分数
     * 时段权重 * 0.7 + 点赞数权重 * 0.3
     */
    private double calculateTimeAndLikeScore(Recipe recipe, int maxLikes) {
        // 时间戳分数 * 0.7
        double timeScore = getCurrentTimeScore() * 0.7;

        // 点赞数分数 * 0.3
        double normalizedLikes = (double) recipe.getLikesCount() / maxLikes;
        double likeScore = normalizedLikes * 0.3;

        return timeScore + likeScore;
    }

    /**
     * 获取用户已点赞的食谱ID集合
     */
    private Set<Long> getLikedRecipeIds(Long userId) {
        if (userId == null) {
            return Collections.emptySet();
        }

        List<Likes> userLikes = likesService.findByUserId(userId);
        return userLikes.stream()
                .map(Likes::getRecipeId)
                .collect(Collectors.toSet());
    }

    /**
     * 计算食谱的总推荐分数
     */
    private double calculateRecipeScore(Long userId, Recipe recipe) {
        // 时间戳分数 * 0.4
        double timeScore = getCurrentTimeScore() * 0.4;

        // 用户偏好分数 * 0.4
        double preferenceScore = getUserPreferenceScore(userId, recipe);

        // 难度权重 * 0.2
        double difficultyWeight = calculateDifficultyWeight(userId, recipe.getDifficulty()) * 0.2;

        return timeScore + preferenceScore + difficultyWeight;
    }

    /**
     * 计算基于当前时间的时间戳分数
     */
    private double getCurrentTimeScore() {
        LocalTime currentTime = LocalTime.now();
        int currentHour = currentTime.getHour();

        double baseScore = 1.0 - Math.abs(12 - currentHour) / 12.0;
        double timeMultiplier = 1.0;
        for (TimePeriodWeight timeWeight : timeWeights.values()) {
            if (timeWeight.contains(currentTime)) {
                timeMultiplier = timeWeight.weight;
                break;
            }
        }

        return baseScore * timeMultiplier;
    }

    /**
     * 计算用户偏好分数（0.4权重）
     */
    private double getUserPreferenceScore(Long userId, Recipe recipe) {
        List<Likes> userLikes = likesService.findByUserId(userId);

        if (userLikes.isEmpty()) {
            return 0.3;
        }

        Set<Long> likedRecipeIds = userLikes.stream()
                .map(Likes::getRecipeId)
                .collect(Collectors.toSet());

        List<Recipe> likedRecipes = recipeRepository.findAllById(likedRecipeIds);

        List<Likes> recentLikes = likesService.findRecentLikesByUserId(userId, 5);
        Set<Long> recentLikedRecipeIds = recentLikes.stream()
                .map(Likes::getRecipeId)
                .collect(Collectors.toSet());
        List<Recipe> recentLikedRecipes = recipeRepository.findAllById(recentLikedRecipeIds);

        Set<String> allTags = new HashSet<>();
        Set<String> allCategories = new HashSet<>();
        Set<String> recentTags = new HashSet<>();
        Set<String> recentCategories = new HashSet<>();

        for (Recipe likedRecipe : likedRecipes) {
            allTags.addAll(getTagNames(likedRecipe.getId()));
            allCategories.addAll(getCategoryNames(likedRecipe.getId()));
        }
        for (Recipe recentRecipe : recentLikedRecipes) {
            recentTags.addAll(getTagNames(recentRecipe.getId()));
            recentCategories.addAll(getCategoryNames(recentRecipe.getId()));
        }

        Set<String> recipeTags = getTagNames(recipe.getId());
        Set<String> recipeCategories = getCategoryNames(recipe.getId());

        double recentTagScore = calculateSimilarity(recipeTags, recentTags) * 0.1;
        double recentCategoryScore = calculateSimilarity(recipeCategories, recentCategories) * 0.1;
        double favoriteTagScore = calculateFavoriteTagScore(recipeTags, likedRecipes) * 0.1;
        double favoriteCategoryScore = calculateFavoriteCategoryScore(recipeCategories, likedRecipes) * 0.1;

        return recentTagScore + recentCategoryScore + favoriteTagScore + favoriteCategoryScore;
    }

    /**
     * 获取食谱的标签名称集合
     */
    private Set<String> getTagNames(Long recipeId) {
        List<Tag> tags = tagRepository.findByRecipeId(recipeId);
        return tags.stream()
                .map(Tag::getName)
                .collect(Collectors.toSet());
    }

    /**
     * 获取食谱的分类名称集合
     */
    private Set<String> getCategoryNames(Long recipeId) {
        List<Category> categories = categoryRepository.findByRecipeId(recipeId);
        return categories.stream()
                .map(Category::getName)
                .collect(Collectors.toSet());
    }

    /**
     * 计算基于用户喜欢食谱难度分布的难度权重
     */
    private double calculateDifficultyWeight(Long userId, Integer targetDifficulty) {
        List<Likes> userLikes = likesService.findByUserId(userId);

        if (userLikes.isEmpty()) {
            return 0.5;
        }

        // 获取用户喜欢的食谱ID
        Set<Long> likedRecipeIds = userLikes.stream()
                .map(Likes::getRecipeId)
                .collect(Collectors.toSet());

        // 获取用户喜欢的完整食谱信息
        List<Recipe> likedRecipes = recipeRepository.findAllById(likedRecipeIds);

        // 提取用户喜欢食谱的难度值
        List<Integer> likedDifficulties = likedRecipes.stream()
                .map(Recipe::getDifficulty)
                .collect(Collectors.toList());

        // 计算难度分布
        Map<Integer, Long> difficultyCount = likedDifficulties.stream()
                .collect(Collectors.groupingBy(d -> d, Collectors.counting()));

        long totalLikes = likedDifficulties.size();

        // 计算用户对目标难度的偏好概率
        long targetCount = difficultyCount.getOrDefault(targetDifficulty, 0L);
        double targetProbability = (double) targetCount / totalLikes;

        // 平滑处理，避免概率为0
        double alpha = 0.1;
        double smoothedProbability = (targetProbability + alpha) / (1 + alpha * difficultyCount.size());

        // 归一化到0-1范围
        double maxProbability = difficultyCount.values().stream()
                .mapToDouble(count -> (double) count / totalLikes)
                .max()
                .orElse(1.0);

        if (maxProbability > 0) {
            return smoothedProbability / maxProbability;
        } else {
            return 0.5;
        }
    }

    /**
     * 计算两个集合的相似度
     */
    private double calculateSimilarity(Set<String> set1, Set<String> set2) {
        if (set1.isEmpty() || set2.isEmpty()) {
            return 0.0;
        }

        Set<String> intersection = new HashSet<>(set1);
        intersection.retainAll(set2);

        return (double) intersection.size() / set1.size();
    }

    /**
     * 计算基于频率的标签偏好分数
     */
    private double calculateFavoriteTagScore(Set<String> recipeTags, List<Recipe> likedRecipes) {
        if (recipeTags.isEmpty() || likedRecipes.isEmpty()) {
            return 0.0;
        }

        // 计算用户对所有标签的频率
        Map<String, Long> tagFrequency = new HashMap<>();
        for (Recipe recipe : likedRecipes) {
            List<Tag> tags = tagRepository.findByRecipeId(recipe.getId());
            for (Tag tag : tags) {
                tagFrequency.merge(tag.getName(), 1L, Long::sum);
            }
        }

        // 获取用户最喜欢的标签（前5个）
        List<String> favoriteTags = tagFrequency.entrySet().stream()
                .sorted((e1, e2) -> Long.compare(e2.getValue(), e1.getValue()))
                .limit(5)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());

        return calculateSimilarity(recipeTags, new HashSet<>(favoriteTags));
    }

    /**
     * 计算基于频率的分类偏好分数
     */
    private double calculateFavoriteCategoryScore(Set<String> recipeCategories, List<Recipe> likedRecipes) {
        if (recipeCategories.isEmpty() || likedRecipes.isEmpty()) {
            return 0.0;
        }

        // 计算用户对所有分类的频率
        Map<String, Long> categoryFrequency = new HashMap<>();
        for (Recipe recipe : likedRecipes) {
            List<Category> categories = categoryRepository.findByRecipeId(recipe.getId());
            for (Category category : categories) {
                categoryFrequency.merge(category.getName(), 1L, Long::sum);
            }
        }

        // 获取用户最喜欢的分类（前3个）
        List<String> favoriteCategories = categoryFrequency.entrySet().stream()
                .sorted((e1, e2) -> Long.compare(e2.getValue(), e1.getValue()))
                .limit(3)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());

        return calculateSimilarity(recipeCategories, new HashSet<>(favoriteCategories));
    }
}